package ace.module.common.core.impl.controller.rest.admin;

import ace.cmp.core.model.R;
import ace.cmp.data.spring.data.jdbc.querydsl.rsql.core.manager.security.SecurityManager;
import ace.cmp.data.spring.data.jdbc.querydsl.rsql.core.manager.security.impl.SecurityManagerImpl;
import ace.cmp.util.AceAssertUtils;
import ace.module.common.api.constant.SystemSecurityConstants;
import ace.module.common.api.model.input.AppTypeInput;
import ace.module.security.api.AuthenticationApi;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import java.io.Serializable;
import java.util.List;
import java.util.Map;
import lombok.AccessLevel;
import lombok.Getter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.web.PageableDefault;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;

/**
 * 管理端-管理API
 * 1、支持管理端管理系统所有相关数据,包含单一租户管理
 * 2、支持租户管理员管理租户相关数据.
 *
 * @author Caspar
 */
public class AdminEntityOfAppController<TEntity, TId extends Serializable, TAppEntity>
    extends AdminTenantEntityOfAppController<TEntity, TId, TAppEntity> {

  public static final String TENANT_ADMIN_SAVE_URI = "/tenant/admin/save";
  public static final String TENANT_ADMIN_SAVE_NOT_NULL_FIELD_URI = "/tenant/admin/saveNotNullField";
  public static final String TENANT_ADMIN_ADD_URI = "/tenant/admin/add";
  public static final String TENANT_ADMIN_ADD_NOT_NULL_FIELD_URI = "/tenant/admin/addNotNullField";
  public static final String TENANT_ADMIN_MODIFY_URI = "/tenant/admin/modify";
  public static final String TENANT_ADMIN_MODIFY_NOT_NULL_FIELD_URI = "/tenant/admin/modifyNotNullField";
  public static final String TENANT_ADMIN_REMOVE_BY_ID_URI = "/tenant/admin/remove";
  public static final String TENANT_ADMIN_REMOVE_LOGIC_BY_ID_URI = "/tenant/admin/removeLogic";
  public static final String TENANT_ADMIN_GET_BY_ID_URI = "/tenant/admin/get";
  public static final String TENANT_ADMIN_GET_LIST_BY_ID_IN_URI = "/tenant/admin/list";
  public static final String TENANT_ADMIN_GET_MAP_BY_ID_IN_URI = "/tenant/admin/map";
  public static final String TENANT_ADMIN_SEARCH_URI = "/tenant/admin/search";

  @Autowired
  @Getter(AccessLevel.PROTECTED)
  private AuthenticationApi authenticationApi;

  @Getter(AccessLevel.PROTECTED)
  private SecurityManager tenantRoleSecurityManager;

  @Override
  public void afterPropertiesSet() throws Exception {
    super.afterPropertiesSet();
    tenantRoleSecurityManager = this.initTenantRoleSecurityManager();
  }

  protected SecurityManager initTenantRoleSecurityManager() {
    return new SecurityManagerImpl(this.getEntityClass(), SystemSecurityConstants.TENANT_ADMIN_SECURITY_AUTHORITY_SUFFIX, this::hasAuthority);
  }

  @Operation(summary = "管理端租户管理-保存,新增或者修改")
  @PostMapping(TENANT_ADMIN_SAVE_URI)
  public R<TId> tenantAdminSave(@Valid @NotNull @RequestBody AppTypeInput<TEntity> input) {
    Long tenantId = this.getAndCheckCurrentTenantId();
    this.getTenantRoleSecurityManager().checkWriteAuthority();
    TEntity entity = input.getData();
    if (this.getManager().isNew(entity)) {
      this.setAndCheckEntityAppId(entity, tenantId, input.getType());
    } else {
      this.checkEntityAppIdEqualByTenantIdAndType(entity, tenantId, input.getType());
    }
    TId id = this.getManager().saveIgnore(entity, true);
    return R.ok(id);
  }

  @Operation(summary = "管理端租户管理-保存,新增或者修改,不为null的字段")
  @PostMapping(TENANT_ADMIN_SAVE_NOT_NULL_FIELD_URI)
  public R<TId> tenantAdminSaveNotNullField(@Valid @NotNull @RequestBody AppTypeInput<TEntity> input) {
    Long tenantId = this.getAndCheckCurrentTenantId();
    this.getTenantRoleSecurityManager().checkWriteAuthority();
    TEntity entity = input.getData();
    if (this.getManager().isNew(entity)) {
      this.setAndCheckEntityAppId(entity, tenantId, input.getType());
    } else {
      this.checkEntityAppIdEqualByTenantIdAndType(entity, tenantId, input.getType());
    }
    TId id = this.getManager().saveIgnore(entity, false);
    return R.ok(id);
  }

  @Operation(summary = "管理端租户管理-新增")
  @PostMapping(TENANT_ADMIN_ADD_URI)
  public R<TId> tenantAdminAdd(@Valid @NotNull @RequestBody AppTypeInput<TEntity> input) {
    Long tenantId = this.getAndCheckCurrentTenantId();
    this.getTenantRoleSecurityManager().checkWriteAuthority();
    TEntity entity = input.getData();
    this.setAndCheckEntityAppId(entity, tenantId, input.getType());
    TId id = this.getManager().addIgnore(entity, true);
    return R.ok(id);
  }

  @Operation(summary = "管理端租户管理-新增,不为null的字段")
  @PostMapping(TENANT_ADMIN_ADD_NOT_NULL_FIELD_URI)
  public R<TId> tenantAdminAddNotNullField(@Valid @NotNull @RequestBody AppTypeInput<TEntity> input) {
    Long tenantId = this.getAndCheckCurrentTenantId();
    this.getTenantRoleSecurityManager().checkWriteAuthority();
    TEntity entity = input.getData();
    this.setAndCheckEntityAppId(entity, tenantId, input.getType());
    TId id = this.getManager().addIgnore(entity, false);
    return R.ok(id);
  }

  @Operation(summary = "管理端租户管理-修改")
  @PostMapping(TENANT_ADMIN_MODIFY_URI)
  public R<TId> tenantAdminModify(@Valid @NotNull @RequestBody AppTypeInput<TEntity> input) {
    Long tenantId = this.getAndCheckCurrentTenantId();
    this.getTenantRoleSecurityManager().checkWriteAuthority();
    TEntity entity = input.getData();
    this.checkEntityAppIdEqualByTenantIdAndType(entity, tenantId, input.getType());
    TId id = this.getManager().modifyIgnore(entity, Boolean.TRUE);
    return R.ok(id);
  }

  @Operation(summary = "管理端租户管理-修改,不为null的字段")
  @PostMapping(TENANT_ADMIN_MODIFY_NOT_NULL_FIELD_URI)
  public R<TId> tenantAdminModifyNotNullField(@Valid @NotNull @RequestBody AppTypeInput<TEntity> input) {
    Long tenantId = this.getAndCheckCurrentTenantId();
    this.getTenantRoleSecurityManager().checkWriteAuthority();
    TEntity entity = input.getData();
    this.checkEntityAppIdEqualByTenantIdAndType(entity, tenantId, input.getType());
    TId id = this.getManager().modifyIgnore(entity, Boolean.FALSE);
    return R.ok(id);
  }

  @Operation(summary = "管理端租户管理-删除")
  @PostMapping(TENANT_ADMIN_REMOVE_BY_ID_URI)
  public R<Boolean> tenantAdminRemove(
      @Valid @NotNull @NotEmpty @RequestBody AppTypeInput<List<TId>> input) {
    Long tenantId = this.getAndCheckCurrentTenantId();
    this.getTenantRoleSecurityManager().checkRemoveAuthority();
    Serializable appId = this.getApplicationManager().getIdAndCheckByOutIdAndOutType(tenantId.toString(), input.getType());
    boolean isSuccess = this.getEntityOfAppManager().removeByAppIdAndIdIn(appId, input.getData());
    return R.ok(isSuccess);
  }

  @Operation(summary = "管理端租户管理-逻辑删除")
  @PostMapping(TENANT_ADMIN_REMOVE_LOGIC_BY_ID_URI)
  public R<Boolean> tenantAdminRemoveLogic(
      @Valid @NotNull @NotEmpty @RequestBody AppTypeInput<List<TId>> input) {
    Long tenantId = this.getAndCheckCurrentTenantId();
    this.getTenantRoleSecurityManager().checkRemoveAuthority();
    Serializable appId = this.getApplicationManager().getIdAndCheckByOutIdAndOutType(tenantId.toString(), input.getType());
    boolean isSuccess = this.getEntityOfAppManager().removeLogicByAppIdIdAndIdIn(appId, input.getData());
    return R.ok(isSuccess);
  }

  @Operation(summary = "管理端租户管理-获取明细")
  @GetMapping(TENANT_ADMIN_GET_BY_ID_URI)
  public R<TEntity> tenantAdminGetById(
      @Parameter(description = "数据类型") @Valid @NotEmpty @RequestParam("type") String type,
      @Valid @NotNull @RequestParam("id") TId id) {
    Long tenantId = this.getAndCheckCurrentTenantId();
    this.getTenantRoleSecurityManager().checkReadAuthority();
    Serializable appId = this.getApplicationManager().getIdAndCheckByOutIdAndOutType(tenantId.toString(), type);
    if (appId == null) {
      return R.ok();
    }
    TEntity entity = this.getEntityOfAppManager().getByAppIdAndId(appId, id);
    return R.ok(entity);
  }

  @Operation(summary = "管理端租户管理-根据id批量获取")
  @GetMapping(TENANT_ADMIN_GET_LIST_BY_ID_IN_URI)
  public R<List<TEntity>> tenantAdminGetListByIdIn(
      @Parameter(description = "数据类型") @Valid @NotEmpty @RequestParam("type") String type,
      @Valid @NotNull @NotEmpty @RequestParam("ids") List<TId> ids) {
    Long tenantId = this.getAndCheckCurrentTenantId();
    this.getTenantRoleSecurityManager().checkReadAuthority();
    Serializable appId = this.getApplicationManager().getIdAndCheckByOutIdAndOutType(tenantId.toString(), type);
    if (appId == null) {
      return R.ok();
    }
    List<TEntity> entities = this.getEntityOfAppManager().getByAppIdAndIds(appId, ids);
    return R.ok(entities);
  }

  @Operation(summary = "管理端租户管理-根据id批量获取,返回Map数据结构，key:实体id,value:实体")
  @GetMapping(TENANT_ADMIN_GET_MAP_BY_ID_IN_URI)
  public R<Map<TId, TEntity>> tenantAdminGetMapByIdIn(
      @Parameter(description = "数据类型") @Valid @NotEmpty @RequestParam("type") String type,
      @Valid @NotNull @NotEmpty @RequestParam("ids") List<TId> ids) {
    Long tenantId = this.getAndCheckCurrentTenantId();
    this.getTenantRoleSecurityManager().checkReadAuthority();
    Serializable appId = this.getApplicationManager().getIdAndCheckByOutIdAndOutType(tenantId.toString(), type);
    if (appId == null) {
      return R.ok();
    }
    Map<TId, TEntity> entityMap = (Map<TId, TEntity>) this.getEntityOfAppManager().getMapByAppIdAndIds(appId, ids);
    return R.ok(entityMap);
  }

  @Operation(summary = "管理端租户管理-搜索", description = "rsql语法格式分页搜索")
  @GetMapping(TENANT_ADMIN_SEARCH_URI)
  public R<Page<TEntity>> tenantAdminSearch(
      @Parameter(description = "数据类型") @Valid @NotEmpty @RequestParam("type") String type,
      @Parameter(description = "rsql语法格式查询条件") @RequestParam(value = "query", required = false) String query,
      @PageableDefault Pageable pageable) {
    Long tenantId = this.getAndCheckCurrentTenantId();
    this.getTenantRoleSecurityManager().checkReadAuthority();
    Serializable appId = this.getApplicationManager().getIdAndCheckByOutIdAndOutType(tenantId.toString(), type);
    if (appId == null) {
      return R.ok();
    }
    Page<TEntity> entityPage = this.getEntityOfAppManager().searchByAppIdAndRsql(appId, query, pageable);
    return R.ok(entityPage);
  }

  protected Long getAndCheckCurrentTenantId() {
    Long tenantId = this.getAuthenticationApi().getCurrentTenantId();
    AceAssertUtils.isNull(tenantId, "cant not find current tenant id.");
    return tenantId;
  }
}
